﻿(function (JFocus) {
    

    /**
    * 动画属性
    **/
    var TweenProperty = function (key, from, to, next) {
        this.key = key;
        this.from = from;
        this.to = to;
        this.diff = to - from;
        this.next = next;
    };

    /**
    * 动作项
    **/
    JFocus.type.TweenJob = function (target) {
        JFocus.type.EventDispatcher.apply(this, [null]);

        /**
        * @inheritDoc
        **/
        this.addEventListener = function (type, listener, target) {
            if (this._observers[type] == null) {
                this._observers[type] = new Array();
            }

            if (!this.hasEventListener(type, listener)) {
                this._observers[type].push({ listener: listener, target: target });
            }

            this._invokeEvent = true;
        };

        /**
        * 获取动画对象
        **/
        this.tweenTarget = function () {
            return this._tweenTarget;
        };

        /**
        * 初始化对象状态
        **/
        this.init = function (curTime) {
            if (curTime == null || curTime == undefined) {
                curTime = -1;
            }

            if (this._initialized) {
                return;
            }
            if (this._finished) {
                return;
            }
            if (this._canceled) {
                return;
            }
            if (this._pausing) {
                return;
            }

            if (curTime < 0) {
                curTime = JFocus.getTimer();
            }
            this._startTime = curTime;
            this._setupValues();
            this._initialized = true;

            if (this.onInit instanceof Function) {
                this.onInit.apply(this.onInit, this.onInitParams);
            }

            if (this._invokeEvent) {
                var evt = new JFocus.type.Event(JFocus.type.Event.INIT);
                this.dispatchEvent(evt);
            }
        };

        /**
        * 解析动画状态
        **/
        this._setupValues = function () {
            var keys = (this.to != null) ? this.to : this.from;
            if (keys == null) return;

            var curr = new Object();
            if (this.from == null || this.to == null) {
                for (var k in keys) {
                    curr[k] = this._tweenTarget[k]();
                }
            }
            var first = (this.from != null) ? this.from : curr;
            var last = (this.to != null) ? this.to : curr;

            var p;
            var lastProp;

            for (var key in keys) {
                if (first[key] == last[key]) continue; // skip this
                p = new TweenProperty(key, first[key], last[key], null);
                if (this._firstProp == null) {
                    this._firstProp = p;
                } else {
                    lastProp.next = p;
                }
                lastProp = p;
            }

            if (this.from != null) {
                this._applyFirstValues();
            }
        };

        /**
        * 应用起始状态
        **/
        this._applyFirstValues = function () {
            var p;
            for (p = this._firstProp; p != null; p = p.next) {
                this._tweenTarget[p.key](p.from);
            }

            if (this.onChange instanceof Function) {
                this.onChange.apply(this.onChange, this.onChangeParams);
            }

            if (this._invokeEvent) {
                var evt = new JFocus.type.Event(JFocus.type.Event.CHANGE);
                this.dispatchEvent(evt);
            }
        };

        /**
        * 应用终止状态
        **/
        this._applyFinalValues = function () {
            var p;
            for (p = this._firstProp; p != null; p = p.next) {
                this._tweenTarget[p.key](p.to);
            }

            if (this.onChange instanceof Function) {
                this.onChange.apply(this.onChange, this.onChangeParams);
            }

            if (this._invokeEvent) {
                var evt = new JFocus.type.Event(JFocus.type.Event.CHANGE);
                this.dispatchEvent(evt);
            }
        };

        /**
        * 转向下一个动画状态
        **/
        this.step = function (curTime) {
            if (this._finished) return;
            if (this._canceled) return;
            if (this._pausing) return;

            if (curTime < 0) {
                curTime = JFocus.getTimer();
            }

            if (!this._initialized) {
                this._init(curTime);
                return;
            }

            // check invoked in the same time
            if (this._lastTime == curTime) return;
            this._lastTime = curTime;

            // check finished
            var secs = (curTime - this._startTime) * 0.001;
            if (secs >= this.duration) {
                if (this.repeat) {
                    if (this.yoyo) {
                        this._reverse = !this._reverse;
                    }
                    secs -= this.duration;
                    this._startTime = curTime - secs * 1000;
                } else {
                    this.complete();
                    return;
                }
            }

            // tweening
            var pos = secs / this.duration;
            if (this._reverse) {
                pos = 1 - pos;
            }
            if (this.ease instanceof Function) {
                pos = this.ease(pos);
            }

            // update
            var p;
            if (this.round) {
                for (p = this._firstProp; p != null; p = p.next) {
                    this._tweenTarget[p.key](Math.round(p.from + p.diff * pos));
                }
            } else {
                for (p = this._firstProp; p != null; p = p.next) {
                    this._tweenTarget[p.key](p.from + p.diff * pos);
                }
            }

            if (this.onChange instanceof Function) {
                this.onChange.apply(this.onChange, this.onChangeParams);
            }

            if (this._invokeEvent) {
                var evt = new JFocus.type.Event(JFocus.type.Event.CHANGE);
                this.dispatchEvent(evt);
            }
        };

        /**
        * 完成动画
        **/
        this.complete = function () {
            if (!this._initialized) return;
            if (this.finished) return;
            if (this._canceled) return;
            if (!this._tweenTarget) return;

            this._applyFinalValues();
            this.finished = true;

            if (this.onComplete instanceof Function) {
                this.onComplete.apply(this.onComplete, this.onCompleteParams);
            }

            if (this._invokeEvent) {
                //派发事件
                var evt = new JFocus.type.Event(JFocus.type.Event.COMPLETE);
                this.dispatchEvent(evt);
            }
        };

        /**
        * 取消动画
        **/
        this.cancel = function () {
            if (!this._initialized) return;
            if (this._canceled) return;
            if (!this._tweenTarget) return;

            this._applyFirstValues();

            this.finished = true;
            this._canceled = true;

            if (this.onCancel instanceof Function) {
                this.onCancel.apply(this.onCancel, this.onCancelParams);
            }

            if (this._invokeEvent) {
                //派发事件
                var evt = new JFocus.type.Event(JFocus.type.Event.CANCEL);
                this.dispatchEvent(evt);
            }
        };

        /**
        * 关闭动画
        **/
        this.close = function () {
            if (!this._initialized) return;
            if (this._canceled) return;

            this.finished = true;

            if (this.onClose instanceof Function) {
                this.onClose.apply(this.onClose, this.onCloseParams);
            }

            if (this._invokeEvent) {
                //派发事件
                var evt = new JFocus.type.Event(JFocus.type.Event.CLOSE);
                this.dispatchEvent(evt);
            }

            this._clearnup();
        };

        /**
        * 重置状态
        **/
        this._clearnup = function () {
            this.onInit = null;
            this.onInitParams = null;

            this.onChange = null;
            this.onChangeParams = null;

            this.onComplete = null;
            this.onCompleteParams = null;

            this.onClose = null;
            this.onCloseParams = null;

            this.onCancel = null;
            this.onCancelParams = null;

            this._firstProp = null;
            this._invokeEvent = false;
        };

        /**
        * 终止动画
        **/
        this.abort = function () {
            this.finished = true;
            this._canceled = true;
            this._clearnup();
        };

        /**
        * 暂停动画
        **/
        this.pause = function () {
            if (this._pausing) return;
            this._pausing = true;
            this._lastTime = JFocus.getTimer();
        };

        /**
        * 恢复动画
        **/
        this.resume = function () {
            if (!this._pausing) return;
            this._pausing = false;
            var curTime = JFocus.getTimer();
            this._startTime = curTime - (this._lastTime - this._startTime);
            this.step(curTime);
        };

        /**
        * 对象字符串描述
        **/
        this.toString = function () {
            return "[TweenJob job]";
        };

        this._tweenTarget = target; 		//动画对象

        this.duration = 1.0; 			//动画持续时间
        this.from = null; 				//起始状态
        this.to = null; 					//终止状态
        this.ease = null; 				//缓冲方法
        this.finished = false; 			//完成标志
        this.round = false; 				//近似整数值
        this.repeat = false; 			//重复执行标志
        this.yoyo = false; 				//反向动作标志
        this.next = null; 				//下一个运动对象（Job对象）

        this._reverse = false;
        this._initialized = false;
        this._canceled = false;
        this._pausing = false;
        this._startTime = 0;
        this._lastTime = 0;
        this._firstProp = null;
        this._invokeEvent = false;

        this.onInit;                //初始化事件处理器（Function）
        this.onInitParams;          //初始化事件参数(Array)

        this.onChange;              //变更事件处理器(Function)
        this.onChangeParams;        //变更事件参数(Array)

        this.onComplete;            //完成事件处理器(Function)
        this.onCompleteParams;      //完成事件参数(Array)

        this.onClose;               //关闭事件处理器(Function)
        this.onCloseParams;         //关闭事件参数（Array）

        this.onCancel;              //取消事件处理器(Function)
        this.onCancelParams;        //取消事件参数(Array)
    };
    JFocus.inherited(JFocus.type.TweenJob, JFocus.type.EventDispatcher);

    /**
    * 动画管理器
    **/
    var TweenManager = function () {
        var self = this;

        /**
        * 使动作项进入动画队列
        * @param job 动作项对象
        * @param delay 延时
        **/
        this.queue = function (job, delay) {
            if (delay > 0) {
                var self = this;
                var closure = function () {
                    self.queue(job);
                };
                setTimeout(closure, delay * 1000);
                return;
            }
            job.init();
            if (this._lastAdded != null) {
                this._lastAdded.next = job;
            } else {
                this._firstAdded = job;
            }
            this._lastAdded = job;
            if (!this._running) this._awake();
        };

        /**
        * 唤醒动画执行
        **/
        this._awake = function () {
            if (this._running) return;
            JFocus.application.addEventListener(JFocus.type.Event.ENTER_FRAME, this._enterFrameHandler);
            this._running = true;
        };

        /**
        * 冻结动画执行
        **/
        this._sleep = function () {
            JFocus.application.removeEventListener(JFocus.type.Event.ENTER_FRAME, this._enterFrameHandler);
            this._running = false;
        };

        /**
        * 动画执行事件
        * @param evt 事件对象
        **/
        this._enterFrameHandler = function (evt) {
            if (self._firstAdded != null) {
                self._mergeList();
            }
            // check all jobs done
            if (self._firstJob == null) {
                self._sleep();
                return;
            }


            var curTime = JFocus.getTimer();
            var prev = null;
            var job = self._firstJob;
            for (job = self._firstJob; job != null; job = job.next) {
                if (job.finished) {
                    if (prev == null) {
                        self._firstJob = job.next;
                    } else {
                        prev.next = job.next;
                    }
                    if (job.next == null) {
                        self._lastJob = prev;
                    }
                    job.close();
                } else {
                    job.step(curTime);
                    prev = job;
                }
            }
        };

        /**
        * 退出动画
        **/
        this.abort = function () {
            this._mergeList();
            for (var job = this._firstJob; job != null; job = job.next) {
                job.abort();
            }
        };

        /**
        * 取消动画
        **/
        this.cancel = function () {
            this._mergeList();
            for (var job = this._firstJob; job != null; job = job.next) {
                job.cancel();
            }
        };

        /**
        * 完成动画
        **/
        this.complete = function () {
            this._mnergeList();
            for (var job = this._firstJob; job != null; job = job.next) {
                job.complete();
            }
        };

        /**
        * 暂停动画
        **/
        this.pause = function () {
            this._mnergeList();
            for (var job = this._firstJob; job != null; job = job.next) {
                job.pause();
            }
        };

        /**
        * 恢复动画
        **/
        this.resume = function () {
            for (var job = this._firstJob; job != null; job = job.next) {
                job.resume();
            }
        };

        this._mergeList = function () {
            if (!this._firstAdded) return;
            if (this._lastJob != null) {
                this._lastJob.next = this._firstAdded;
            } else {
                this._firstJob = this._firstAdded;
            }
            this._lastJob = this._lastAdded;
            this._firstAdded = null;
            this._lastAdded = null;
        };

        this.toString = function () {
            return "[TweenManager manager]";
        };

        this._running = false;
        this._firstJob = null;
        this._lastJob = null;
        this._firstAdded = null;
        this._lastAdded = null;
    };

    /**
    * 动画类
    **/
    var Tween = function () {
    };

    Tween.manager = new TweenManager();

    /**
    * 指定从某个状态过渡到当前状态
    * @param target 动画对象
    * @param duration 动画持续时间
    * @param from 初始状态
    * @param ease 缓冲类型
    * @param callback 回调事件
    * @return 动作项对象
    **/
    Tween.from = function (target, duration, from, ease, callback) {
        var job = new JFocus.type.TweenJob(target);
        job.from = from;
        job.duration = duration;
        if (ease != null) job.ease = ease;
        job.onClose = callback;
        Tween.queue(job);
        return job;
    };

    /**
    * 指定从当前状态过渡到指定状态
    * @param target 动画对象
    * @param duration 动画持续时间
    * @param to 终止状态
    * @param ease 缓冲类型
    * @param callback 回调事件
    * @return 动作项对象
    **/
    Tween.to = function (target, duration, to, ease, callback) {
        var job = new JFocus.type.TweenJob(target);
        job.to = to;
        job.duration = duration;
        if (ease != null) job.ease = ease;
        job.onClose = callback;
        Tween.queue(job);
        return job;
    };

    /**
    * 指定从指定的初始状态过渡到指定状态
    * @param target 动画对象
    * @param duration 动画持续时间
    * @param from 初始状态
    * @param to 终止状态
    * @param ease 缓冲类型
    * @param callback 回调事件
    * @return 动作项对象
    **/
    Tween.fromTo = function (target, duration, from, to, ease, callback) {
        var job = new JFocus.type.TweenJob(target);
        job.from = from;
        job.to = to;
        job.duration = duration;
        if (ease != null) job.ease = ease;
        job.onClose = callback;
        Tween.queue(job);
        return job;
    };

    /**
    * 使动作项进入执行队列
    * @param job 动作项对象
    * @param delay 延时
    **/
    Tween.queue = function (job, delay) {
        Tween.manager.queue(job, delay);
    };

    /**
    * 退出动画
    **/
    Tween.abort = function () {
        Tween.manager.abort();
    };

    /**
    * 取消动画
    **/
    Tween.cancel = function () {
        Tween.manager.cancel();
    };

    /**
    * 完成动画
    **/
    Tween.complete = function () {
        Tween.manager.complete();
    };

    /**
    * 暂停动画
    **/
    Tween.pause = function () {
        Tween.manager.pause();
    };

    /**
    * 恢复动画
    **/
    Tween.resume = function () {
        Tween.manager.resume();
    };

    JFocus.tween = Tween;

    /**
    * Back
    **/
    JFocus.type.EaseBack = function () {
    };

    JFocus.type.EaseBack.easeIn = function (t) {
        return 3 * t * t * t - 2 * t * t;
    };

    JFocus.type.EaseBack.easeOut = function (t) {
        return 1.0 - JFocus.type.EaseBack.easeIn(1.0 - t);
    };

    JFocus.type.EaseBack.easeInOut = function (t) {
        return (t < 0.5) ? JFocus.type.EaseBack.easeIn(t * 2.0) * 0.5 : 1 - JFocus.type.EaseBack.easeIn(2.0 - t * 2.0) * 0.5;
    };

    /**
    * EaseBounce
    **/
    JFocus.type.EaseBounce = function () {
    };
    JFocus.type.EaseBounce._DH = 1 / 22;
    JFocus.type.EaseBounce._D1 = 1 / 11;
    JFocus.type.EaseBounce._D2 = 2 / 11;
    JFocus.type.EaseBounce._D3 = 3 / 11;
    JFocus.type.EaseBounce._D4 = 4 / 11;
    JFocus.type.EaseBounce._D5 = 5 / 11;
    JFocus.type.EaseBounce._D7 = 7 / 11;
    JFocus.type.EaseBounce._IH = 1 / JFocus.type.EaseBounce._DH;
    JFocus.type.EaseBounce._I1 = 1 / JFocus.type.EaseBounce._D1;
    JFocus.type.EaseBounce._I2 = 1 / JFocus.type.EaseBounce._D2;
    JFocus.type.EaseBounce._I4D = 1 / JFocus.type.EaseBounce._D4 / JFocus.type.EaseBounce._D4;

    JFocus.type.EaseBounce.easeIn = function (t) {
        var s;
        if (t < JFocus.type.EaseBounce._D1) {
            s = t - JFocus.type.EaseBounce._DH;
            s = JFocus.type.EaseBounce._DH - s * s * JFocus.type.EaseBounce._IH;
        } else if (t < JFocus.type.EaseBounce._D3) {
            s = t - JFocus.type.EaseBounce._D2;
            s = JFocus.type.EaseBounce._D1 - s * s * JFocus.type.EaseBounce._I1;
        } else if (t < JFocus.type.EaseBounce._D7) {
            s = t - JFocus.type.EaseBounce._D5;
            s = JFocus.type.EaseBounce._D2 - s * s * JFocus.type.EaseBounce._I2;
        } else {
            s = t - 1;
            s = 1 - s * s * JFocus.type.EaseBounce._I4D;
        }
        return s;
    };

    JFocus.type.EaseBounce.easeOut = function (t) {
        return 1.0 - JFocus.type.EaseBounce.easeIn(1.0 - t);
    };

    JFocus.type.EaseBounce.easeInOut = function (t) {
        return (t < 0.5) ? JFocus.type.EaseBounce.easeIn(t * 2.0) * 0.5 : 1 - JFocus.type.EaseBounce.easeIn(2.0 - t * 2.0) * 0.5;
    };

    /**
    * Circ
    **/
    JFocus.type.EaseCirc = function () {
    };

    JFocus.type.EaseCirc.easeIn = function (t) {
        return 1.0 - Math.sqrt(1.0 - t * t);
    };

    JFocus.type.EaseCirc.easeOut = function (t) {
        return 1.0 - JFocus.type.EaseCirc.easeIn(1.0 - t);
    };

    JFocus.type.EaseCirc.easeInOut = function (t) {
        return (t < 0.5) ? JFocus.type.EaseCirc.easeIn(t * 2.0) * 0.5 : 1 - JFocus.type.EaseCirc.easeIn(2.0 - t * 2.0) * 0.5;
    };

    /**
    * Cubic
    **/
    JFocus.type.EaseCubic = function () {
    };

    JFocus.type.EaseCubic.easeIn = function (t) {
        return t * t * t;
    };

    JFocus.type.EaseCubic.easeOut = function (t) {
        return 1.0 - JFocus.type.EaseCubic.easeIn(1.0 - t);
    };

    JFocus.type.EaseCubic.easeInOut = function (t) {
        return (t < 0.5) ? JFocus.type.EaseCubic.easeIn(t * 2.0) * 0.5 : 1 - JFocus.type.EaseCubic.easeIn(2.0 - t * 2.0) * 0.5;
    };

    /**
    * Elastic
    **/
    JFocus.type.EaseElastic = function () {
    };

    JFocus.type.EaseElastic.easeIn = function (t) {
        return 1.0 - JFocus.type.EaseElastic.easeOut(1.0 - t);
    };

    JFocus.type.EaseElastic.easeOut = function (t) {
        var s = 1 - t;
        return 1 - Math.pow(s, 8) + Math.sin(t * t * 6 * Math.PI) * s * s;
    };

    JFocus.type.EaseElastic.easeInOut = function (t) {
        return (t < 0.5) ? JFocus.type.EaseElastic.easeIn(t * 2.0) * 0.5 : 1 - JFocus.type.EaseElastic.easeIn(2.0 - t * 2.0) * 0.5;
    };

    /**
    * Expo
    **/
    JFocus.type.EaseExpo = function () {
    };

    JFocus.type.EaseExpo.easeIn = function (t) {
        return Math.pow(2, 10 * (t - 1));
    };

    JFocus.type.EaseExpo.easeOut = function (t) {
        return 1.0 - Math.pow(2, -10 * t);
    };

    JFocus.type.EaseExpo.easeInOut = function (t) {
        return (t < 0.5) ? JFocus.type.EaseExpo.easeIn(t * 2.0) * 0.5 : 1 - JFocus.type.EaseExpo.easeIn(2.0 - t * 2.0) * 0.5;
    };

    /**
    * Linear
    **/
    JFocus.type.EaseLinear = function () {
    };

    JFocus.type.EaseLinear.easeIn = function (t) {
        return t;
    };

    JFocus.type.EaseLinear.easeOut = function (t) {
        return t;
    };

    JFocus.type.EaseLinear.easeInOut = function (t) {
        return t;
    };

    /**
    * Quad
    **/
    JFocus.type.EaseQuad = function () {
    };

    JFocus.type.EaseQuad.easeIn = function (t) {
        return t * t;
    };

    JFocus.type.EaseQuad.easeOut = function (t) {
        return 1.0 - JFocus.type.EaseQuad.easeIn(1.0 - t);
    };

    JFocus.type.EaseQuad.easeInOut = function (t) {
        return (t < 0.5) ? JFocus.type.EaseQuad.easeIn(t * 2.0) * 0.5 : 1 - JFocus.type.EaseQuad.easeIn(2.0 - t * 2.0) * 0.5;
    };

    /**
    * Quart
    **/
    JFocus.type.EaseQuart = function () {
    };

    JFocus.type.EaseQuart.easeIn = function (t) {
        return t * t * t * t;
    };

    JFocus.type.EaseQuart.easeOut = function (t) {
        return 1.0 - JFocus.type.EaseQuart.easeIn(1.0 - t);
    };

    JFocus.type.EaseQuart.easeInOut = function (t) {
        return (t < 0.5) ? JFocus.type.EaseQuart.easeIn(t * 2.0) * 0.5 : 1 - JFocus.type.EaseQuart.easeIn(2.0 - t * 2.0) * 0.5;
    };

    /**
    * Quint
    **/
    JFocus.type.EaseQuint = function () {
    };

    JFocus.type.EaseQuint.easeIn = function (t) {
        return t * t * t * t * t;
    };

    JFocus.type.EaseQuint.easeOut = function (t) {
        return 1.0 - JFocus.type.EaseQuint.easeIn(1.0 - t);
    };

    JFocus.type.EaseQuint.easeInOut = function (t) {
        return (t < 0.5) ? JFocus.type.EaseQuint.easeIn(t * 2.0) * 0.5 : 1 - JFocus.type.EaseQuint.easeIn(2.0 - t * 2.0) * 0.5;
    };

    /**
    * Sine
    **/
    JFocus.type.EaseSine = function () {
    };

    JFocus.type.EaseSine._HALF_PI = Math.PI / 2;

    JFocus.type.EaseSine.easeIn = function (t) {
        return 1.0 - Math.cos(t * JFocus.type.EaseSine._HALF_PI);
    };

    JFocus.type.EaseSine.easeOut = function (t) {
        return 1.0 - JFocus.type.EaseSine.easeIn(1.0 - t);
    };

    JFocus.type.EaseSine.easeInOut = function (t) {
        return (t < 0.5) ? JFocus.type.EaseSine.easeIn(t * 2.0) * 0.5 : 1 - JFocus.type.EaseSine.easeIn(2.0 - t * 2.0) * 0.5;
    };
})(window.JFocus);